/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.neIsInstance;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.EditPolicy;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Continuation;
import org.eclipse.uml2.uml.DestructionEvent;
import org.eclipse.uml2.uml.ExecutionOccurrenceSpecification;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.InteractionUse;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.StateInvariant;
import org.eclipse.uml2.uml.TimeObservation;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.DecideInteractionBoundsCommand;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.DeletionEditPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.FrameLayoutPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.GraphConnectionPolicy;
import com.cea.papyrus.diagram.sequence.sedi.figures.AnchoringFigure;
import com.cea.papyrus.diagram.sequence.sedi.figures.InteractionFigure;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Edit part for Interaction.
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public class FrameEditPart extends AnchoringEditPart<Interaction> {

	public FrameEditPart(GraphElement view, ModelManager modelManager) {
		super(view, modelManager);
	}

	@Override
	protected IFigure createFigure() {
		AnchoringFigure figure = new InteractionFigure();
		figure.setMessageAccess(this);
		return figure;
	}

	@Override
	protected void createEditPolicies() {
		super.createEditPolicies();

		installEditPolicy(EditPolicy.LAYOUT_ROLE, new FrameLayoutPolicy());
//		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE,
//		        new SeDiNodeEditPolicy());
		
		// DeletionEditPolicy (replace SeDiSubpartEditPolicy)
		installEditPolicy(
				EditPolicy.COMPONENT_ROLE,
		        new DeletionEditPolicy());
		// Manage connection between this element and others
		this.installEditPolicy(
				EditPolicy.GRAPHICAL_NODE_ROLE, 
				new GraphConnectionPolicy() );
	}

	@SuppressWarnings("unchecked")
	@Override
	protected List getModelChildren() {
		ArrayList contained= new ArrayList();
			
		// if the presentation of the element is not shape we can display children, otherwise we can't
		if (getGraphNode().getSemanticModel() != null) {
			if ((getGraphNode().getSemanticModel().getPresentation() == null) ||
					!(getGraphNode().getSemanticModel().getPresentation().equals(ModelerHelper.IMAGE_STEREOTYPE_PRESENTATION))) {
				
				Iterator it=getGraphNode().getContained().iterator();
				while (it.hasNext()){
					GraphElement ge=(GraphElement)it.next();
					if (ge instanceof GraphNode) {
						contained.add(ge);	
					}			
				}
			}
		}
//		return contained;
		return orderChildren(contained);
	}
	
//	@SuppressWarnings("unchecked")
//	@Override
//	protected List getModelChildren() {
//		final List childrenViews = collectChildren();
//		return orderChildren(childrenViews);
//	}

//	@Deprecated
//	@SuppressWarnings("unchecked")
//	private List collectChildren() {
//		final List childrenViews = new LinkedList();
//
//		final GraphNode interactionView = (GraphNode) getModel();
//		final Diagram diagram = LookForElement.getDiagram(interactionView);
//
//		// TODO The code below can go into a handler
//		diagram.eSetDeliver(false);
//		try {
//			final ModelSynchronizer synchronizer =
//			        new ModelSynchronizer(diagram, this);
//
//			// Children of Interaction Frame are:
//			// Lifelines, InteractionOccurrence, StateInvariant, Continuation,
//			// Stop, Comments;
//			// Also: InteractionConstraints (guards) of interaction operands
//
//			for (GraphElement element : (List<GraphElement>) ModelUtil
//			        .getContents(diagram)) {
//				if (neIsInstance(element, Lifeline.class)
//				        || neIsInstance(element, InteractionUse.class)
//				        || neIsInstance(element, StateInvariant.class)
//				        || neIsInstance(element, Continuation.class)
//				        || (neIsInstance(element, OccurrenceSpecification.class) && neEventIsInstance(
//				                element, DestructionEvent.class))
//				        || neIsInstance(element, ExecutionSpecification.class)
//				        || neIsInstance(element, Comment.class)
//				        // || neIsInstance(element,
//				        // InteractionConstraint.class))
//				        // garbage collection
//				        || neIsNullInstance(element)
//				        || (neIsInstance(element, OccurrenceSpecification.class) && neEventIsNull(element)))
//					synchronizer.registerElement(element);
//			}
//
//			// We use a queue to handle the recursive aspect of the search
//			LinkedList<InteractionFragment> queue =
//			        new LinkedList<InteractionFragment>();
//			queue.add(ModelUtil
//			        .<InteractionFragment> getSemantic(interactionView));
//
//			while (!queue.isEmpty()) {
//				final InteractionFragment scope = queue.removeFirst();
//
//				final List<? extends NamedElement> elements;
//				if (scope instanceof Interaction) {
//					final Interaction interaction = (Interaction) scope;
//
//					for (Lifeline lifeline : getLifelines(interaction)) {
//						GraphElement ge =
//						        synchronizer.synchronizeElement(lifeline);
//						if (ge != null) {
//							childrenViews.add(ge);
//						}
//					}
//
//					for (Comment comment : getOwnedComments(interaction)) {
//						GraphElement ge =
//						        synchronizer.synchronizeElement(comment);
//						if (ge != null) {
//							childrenViews.add(ge);
//						}
//					}
//
//					elements = interaction.getFragments();
//				} else {
//					final InteractionOperand operand =
//					        (InteractionOperand) scope;
//					elements = operand.getFragments();
//				}
//
//				for (NamedElement element : elements) {
//					if (element instanceof InteractionUse
//					        || element instanceof StateInvariant
//					        || element instanceof Continuation
//					        || (element instanceof OccurrenceSpecification && ((OccurrenceSpecification) element)
//					                .getEvent() instanceof DestructionEvent)
//					        || element instanceof ExecutionSpecification) {
//						// || element instanceof
//						// ExecutionOccurrenceSpecification
//						// || element instanceof DestructionEvent)
//						GraphElement ge =
//						        synchronizer.synchronizeElement(element);
//						if (ge != null) {
//							childrenViews.add(ge);
//						}
//					} else if (element instanceof CombinedFragment)
//						queue
//						        .addAll(((CombinedFragment) element)
//						                .getOperands());
//				}
//			}
//
//			synchronizer.cleanup();
//		} finally {
//			diagram.eSetDeliver(true);
//		}
//
//		return childrenViews;
//	}

	@SuppressWarnings("unchecked")
	private List orderChildren(final List childrenViews) {
		// We care about the order of the children because it affects their
		// visual z-order. Early children appear at the bottom of the visual
		// representation.
		//
		// Therefore children should appear in this order:
		// Lifelines, EOs, IUs, other, StateInvariant, Comment
		//
		// The sort algorithm should be stable - meaning that the relative
		// order of the childrenViews of the same type should be the same in
		// both the unsorted and sorted list: If lifeline1 < lifeline2 in
		// the unsorted list, then the same relationship should hold in
		// the sorted list.
		final List lifelines = new ArrayList(childrenViews.size());
		final List interactionUses = new ArrayList();
		final List stateinvariants = new ArrayList();
		final List continuations = new ArrayList();
		final List stops = new ArrayList();
		final List eos = new ArrayList();
		final List other = new ArrayList();
		final List comments = new ArrayList();

		for (Object child : childrenViews) {
			if (neIsInstance(child, Lifeline.class))
				lifelines.add(child);
			else if (neIsInstance(child, InteractionUse.class))
				interactionUses.add(child);
			else if (neIsInstance(child, StateInvariant.class))
				stateinvariants.add(child);
			else if (neIsInstance(child, Continuation.class))
				continuations.add(child);
			else if (neIsInstance(child, DestructionEvent.class))
				stops.add(child);
			else if (neIsInstance(child, ExecutionOccurrenceSpecification.class))
				eos.add(child);
			else if (neIsInstance(child, Comment.class))
				comments.add(child);
			else if (neIsInstance(child, TimeObservation.class))
				comments.add(child);
			else
				other.add(child);
		}

		final List children = lifelines;
		children.addAll(other);
		children.addAll(stateinvariants);
		children.addAll(stops);
		children.addAll(eos);
		children.addAll(interactionUses);
		children.addAll(continuations);
		children.addAll(comments);
		return children;
	}

	@Override
	protected void umlModelChanged(Notification evt) {
		switch (evt.getFeatureID(UMLPackage.class)) {
		case UMLPackage.ELEMENT__OWNED_COMMENT:
		case UMLPackage.INTERACTION__LIFELINE:
		case UMLPackage.INTERACTION__FRAGMENT:
		case UMLPackage.COMBINED_FRAGMENT__OPERAND:
		case UMLPackage.INTERACTION_OPERAND__FRAGMENT:
			switch (evt.getEventType()) {
			case Notification.ADD:
			case Notification.ADD_MANY:
			case Notification.REMOVE:
			case Notification.REMOVE_MANY:
//				if (evt.getOldValue() != null)
//					maybeRemoveUmlAdapter(evt.getOldValue());
//				if (evt.getNewValue() != null)
//					maybeAddUmlAdapter(evt.getNewValue());
//				// fallthrough
			case Notification.MOVE:
				refreshChildren();
			}
		case UMLPackage.INTERACTION__NAME:
			refreshVisuals();
			break;
		default:
			super.umlModelChanged(evt);
		}
	}

	
    // --- Activation ---
    @SuppressWarnings("unchecked")
    @Override
    public void activate() {
		if (this.isActive()) {
			return;
		}
		super.activate();
		// start listening for changes in the model
		((Notifier)this.getModel()).eAdapters().add(graphElementListener);
		Iterator propertiesIterator= ((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier)propertiesIterator.next()).eAdapters().add(graphElementListener);
		}
    }

    @Override
    public void deactivate() {
		if (!this.isActive()) {
			return;
		}

		// stop listening for changes in the model
		((Notifier)this.getModel()).eAdapters().remove(graphElementListener);
		Iterator propertiesIterator= ((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier) propertiesIterator.next()).eAdapters().remove(graphElementListener);
		}		
		
		super.deactivate();
    }
    
    
//	// --- Activation ---
//
//	@Override
//	public void activate() {
//		if (!isActive()) {
//			super.activate();
//
//			// search for elements to "adapt". We want adapters on the
//			// interaction and on all InteractionOperands contained
//			// in the interaction - no matter how deeply nested
//			// In order to listen to all operands, also newly created
//			// once, we also need listeners on combined fragments
//			maybeAddUmlAdapter(getUmlElement());
//		}
//	}
//
//	@Override
//	public void deactivate() {
//		maybeRemoveUmlAdapter(getUmlElement());
//		super.deactivate();
//	}

//	private void maybeAddUmlAdapter(Object o) {
//		if (o instanceof InteractionOperand) {
//			addUmlAdapter((EObject) o);
//			maybeAddUmlAdapter(getFragments((InteractionOperand) o));
//		} else if (o instanceof CombinedFragment) {
//			addUmlAdapter((EObject) o);
//			maybeAddUmlAdapter(getOperands((CombinedFragment) o));
//		} else if (o instanceof Interaction) {
//			if (o != getUmlElement()) // this adapter has been added by super
//				addUmlAdapter(o);
//			maybeAddUmlAdapter(getFragments((Interaction) o));
//		} else if (o instanceof Comment)
//			addUmlAdapter(o);
//		else if (o instanceof List) {
//			for (Object child : (List<?>) o)
//				maybeAddUmlAdapter(child);
//		}
//	}
//
//	private void maybeRemoveUmlAdapter(Object o) {
//		if (o instanceof InteractionOperand) {
//			removeUmlAdapter((EObject) o);
//			maybeRemoveUmlAdapter(getFragments((InteractionOperand) o));
//		} else if (o instanceof CombinedFragment) {
//			removeUmlAdapter((EObject) o);
//			maybeRemoveUmlAdapter(getOperands((CombinedFragment) o));
//		} else if (o instanceof Interaction) {
//			if (o != getUmlElement()) // this adapter will be removed by super
//				removeUmlAdapter((EObject) o);
//			maybeRemoveUmlAdapter(getFragments((Interaction) o));
//		} else if (o instanceof Comment)
//			removeUmlAdapter(o);
//		else if (o instanceof List) {
//			for (Object child : (List<?>) o)
//				maybeRemoveUmlAdapter(child);
//		}
//	}
//
//	@SuppressWarnings("unchecked")
//	private void addUmlAdapter(Object o) {
//		((EObject) o).eAdapters().add(umlAdapter);
//	}
//
//	private void removeUmlAdapter(Object o) {
//		((EObject) o).eAdapters().remove(umlAdapter);
//	}

	protected void refreshVisualsBounds() {		
		GraphNode node = (GraphNode) getModel();
		if (node.getSize() == null || node.getPosition() == null) {
			DecideInteractionBoundsCommand decideInteractionBoundsCommand =
			        new DecideInteractionBoundsCommand();
			decideInteractionBoundsCommand.setAnyEditPart(this);
			decideInteractionBoundsCommand.execute();
			node.setSize(decideInteractionBoundsCommand.getPreferredBounds()
			        .getSize());
			node.setPosition(decideInteractionBoundsCommand
			        .getPreferredBounds().getLocation());
			Rectangle bounds =
			        decideInteractionBoundsCommand.getPreferredBounds();
			Rectangle preferred =
			        ((AnchoringFigure) getFigure()).getPreferredBounds(bounds);
			if (!bounds.equals(preferred)) {
				node.setSize(preferred.getSize());
				node.setPosition(preferred.getLocation());
			}
			figure.setBounds(preferred);
		} else {
			super.refreshVisualsBounds();
		}
	}
}
